package Helper;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;


import org.eclipse.paho.client.mqttv3.IMqttClient;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.json.JSONObject;


import java.util.UUID;

import java.util.concurrent.ExecutorService;


import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;


import Const.PublicDefine;
import Factory.Factory;
import Interface.IAsynCallBackListener;
import Interface.ICommProtocol;

/**
 *
 * Description:
 * @author admin
 * 2017年2月10日下午17:50:15
 */

public  class MQTT implements MqttCallback {

    /**
     * 线程执行器
     */
    private ExecutorService SingleThread;
    //public static final String HOST = "tcp://60.205.113.86:61613";
    public static final String HOST = String.format("tcp://%s.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883","a1kXcDEn1Q4");


    //private static final String clientid = "client11";
    private IMqttClient client;
    private MqttConnectOptions options;
    private String mqttRecvData;
    private Object synObj=new Object();
    private String TAG="MQTT";
    public IAsynCallBackListener callBack;
    private String topic = "";

    public IMqttClient getClient() {
        return client;
    }
    private String ProductKey;
    private String DeviceName;
    private String DeviceSecret;
    private Context ctx;

    private void RegisterDevice(final String deviceName)
    {
        try {
            ICommProtocol commProtocol = Factory.GetInstance(CommProtocol.class, new Object[]{ctx});
            String url = String.format("%s/AliDevice/QueryDeviceDetail?DeviceName=%s", PublicDefine.UpdateHost, deviceName);
            commProtocol.Get(url, new IAsynCallBackListener() {
                @Override
                public void onFinish(Object sender, Object data) {
                    try {
                        JSONObject json = new JSONObject(data.toString());
                        if (json.getInt("Result")==0)
                        {
                            json=new JSONObject(json.getString("JsonData"));
                            if (json.getBoolean("Success"))
                            {
                                DeviceName= json.getJSONObject("Data").getString("DeviceName");
                                ProductKey=json.getJSONObject("Data").getString("ProductKey");
                                DeviceSecret=json.getJSONObject("Data").getString("DeviceSecret");
                                new Thread(new Runnable() {
                                    @Override
                                    public void run() {
                                        start(deviceName,ProductKey,DeviceSecret);
                                    }
                                }).start();
                            }
                            else
                            {
                                if (json.getString("Code").equals("iot.device.NotExistedDevice"))
                                {
                                    String url = String.format("%s/AliDevice/RegisterDevice?DeviceName=%s&nikName=%s", PublicDefine.UpdateHost, deviceName,deviceName);
                                    ICommProtocol commProtocol = Factory.GetInstance(CommProtocol.class, new Object[]{ctx});
                                    commProtocol.Get(url, new IAsynCallBackListener() {
                                        @Override
                                        public void onFinish(Object sender, Object data) {
                                            try {
                                                JSONObject json = new JSONObject(data.toString());
                                                if (json.getInt("Result") == 0) {
                                                    json = new JSONObject(json.getString("JsonData"));
                                                    if (json.getBoolean("Success")) {
                                                        DeviceName = json.getJSONObject("Data").getString("DeviceName");
                                                        ProductKey = json.getJSONObject("Data").getString("ProductKey");
                                                        DeviceSecret = json.getJSONObject("Data").getString("DeviceSecret");
                                                        new Thread(new Runnable() {
                                                            @Override
                                                            public void run() {
                                                                start(deviceName, ProductKey, DeviceSecret);
                                                            }
                                                        }).start();
                                                    }
                                                }
                                            }
                                            catch (Exception e)
                                            {

                                            }
                                        }

                                        @Override
                                        public void onError(Object sender, Exception e) {

                                        }
                                    });
                                }
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onError(Object sender, Exception e) {

                }
            });
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public MQTT(Context context,String DeviceName, IAsynCallBackListener callBack)
    {
        super();
        this.ctx = context;
        this.callBack = callBack;
        RegisterDevice(DeviceName);
        //this.topic = topic;

    }
    private String hmac_sha1(String key, String datas)
    {
        String reString = "";

        try
        {
            byte[] data = key.getBytes("UTF-8");
            //根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
            SecretKey secretKey = new SecretKeySpec(data, "HmacSHA1");
            //生成一个指定 Mac 算法 的 Mac 对象
            Mac mac = Mac.getInstance("HmacSHA1");
            //用给定密钥初始化 Mac 对象
            mac.init(secretKey);

            byte[] text = datas.getBytes("UTF-8");
            //完成 Mac 操作
            byte[] text1 = mac.doFinal(text);

            //reString = Base64.encodeToString(text1, Base64.DEFAULT);
            reString=EnjoyTools.ByteArrayToHexString(text1);

        } catch (Exception e)
        {
            // TODO: handle exception
        }

        return reString;
    }

    public String HmacSha1Sign(String DeviceSecret, String DeviceName,
                               String ProductKey,long timestramp)
    {
        String data=String.format("clientId%sdeviceName%sproductKey%stimestamp%d", DeviceName, DeviceName, ProductKey,timestramp);

        return hmac_sha1(DeviceSecret,data);
    }

    public  void start(String DeviceName, String ProductKey,String DeviceSecret) {
        try {
        	UUID  uid= UUID.randomUUID();
            String cid=DeviceName;
        	System.out.println(cid);
            MqttClientPersistence persistence= Factory.GetInstance(MemoryPersistence.class,null);
            // host为主机名，clientid即连接MQTT的客户端ID，一般以唯一标识符表示，MemoryPersistence设置clientid的保存形式，默认为以内存保存
            long timestramp = EnjoyTools.GetTimestamp();
            //timestramp=18223342;
            String  clientId=String.format("%s%s",cid,String.format("|securemode=3,signmethod=hmacsha1,ext=1,timestamp=%d|", timestramp));
            client =new MqttClient(HOST, clientId, persistence);

            // MQTT的连接设置
            options = new MqttConnectOptions();
            // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录，这里设置为true表示每次连接到服务器都以新的身份连接
            options.setCleanSession(true);
            // 设置连接的用户名
            options.setUserName(String.format("%s&%s",DeviceName,ProductKey));
            // 设置连接的密码
            String passWord=HmacSha1Sign(DeviceSecret,DeviceName,ProductKey,timestramp);
            options.setPassword(passWord.toCharArray());
            // 设置超时时间 单位为秒
            options.setConnectionTimeout(10);
            // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线，但这个方法并没有重连的机制
            options.setKeepAliveInterval(30);

            //options.setWill(topic, "close".getBytes(), 2, true);
            Log.write(TAG,String.format("主机地址：%s, 设备ID：%s, UName:%s,PWD:%s",client.getServerURI(),
                    client.getClientId(),options.getUserName(),options.getPassword()));
            client.connect(options);
            if (client.isConnected()) {
                //订阅消息
                Log.write(TAG,String.format("连接成功，准备订阅：%s",topic));
                client.setCallback(this);
                client.subscribe(String.format("/%s/$d/user/get",ProductKey,DeviceName), 1);
            }
            else
            {
                Log.write(TAG,"连接失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    public void SendData(JSONObject json, final String topic) throws Exception {
        Log.write(TAG,String.format("发布数据：%s",json.toString()));
        if (!client.isConnected())
        {
            Log.write(TAG,"Mqtt未连接，不发布数据");
            throw new Exception("Mqtt未连接，不发布数据");
        }
        final MqttMessage msg=new MqttMessage();
        msg.setQos(0);
        msg.setPayload(json.toString().getBytes());
        client.publish(topic,msg);
    }
    public void SendData(String topic,byte[] data) throws Exception {
        //Log.write(TAG,String.format("发布数据：%s",EnjoyTools.ByteArrayToHexString(data)));
        if (!client.isConnected())
        {
            Log.write(TAG,"Mqtt未连接，不发布数据");
            throw new Exception("Mqtt未连接，不发布数据");
        }
        final MqttMessage msg=new MqttMessage();
        msg.setQos(0);
        msg.setPayload(data);
        client.publish(topic,msg);
    }


    @Override
    public void connectionLost(Throwable throwable) {

        System.out.println("连接断开，可以做重连");
        /*new Thread(new Runnable() {
            @Override
            public void run() {
                start();
            }
        }).start();
*/
    }

    //在主线程里面处理消息并更新UI界面
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            String topic=msg.getData().getString("topic");
            callBack.onFinish(topic,msg.obj);
        }
    };

    @Override
    public void messageArrived(final  String topic,final MqttMessage message) throws Exception {
        Log.d(TAG,String.format("收到MQTT数据：%s",message.toString()));
        Bundle data=new Bundle();
        data.putString("topic",topic);
        Message msg=new Message();
        msg.obj=message;
        msg.setData(data);
        mHandler.sendMessage(msg);
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        System.out.println("deliveryComplete---------" + token.isComplete());
    }

    public void disconnent(){
        try {
            client.disconnect();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}



